<?php
declare(strict_types=1);
/**
 * MineAdmin is committed to providing solutions for quickly building web applications
 * Please view the LICENSE file that was distributed with this source code,
 * For the full copyright and license information.
 * Thank you very much for using MineAdmin.
 *
 * @Author X.Mo<root@imoi.cn>
 * @Link   https://gitee.com/xmo/MineAdmin
 */

namespace Plugin\Organization\Service;

use App\Exception\BusinessException;
use App\Http\Common\ResultCode;
use App\Model\Enums\User\Type;
use App\Repository\AttachmentRepository;
use App\Repository\Permission\UserRepository;
use App\Service\IService;
use App\Service\Permission\RoleService;
use App\Service\SmsService;
use Common\Common;
use EasyWeChat\MiniApp\Application;
use Hyperf\Collection\Arr;
use Hyperf\DbConnection\Db;
use Mine\Jwt\Factory;
use Mine\Jwt\JwtInterface;
use Plugin\Organization\Repository\RdOrganizeEnterpriseRepository;
use Plugin\Organization\Repository\RdOrganizeUserRepository;

/**
 * 工区账号公司关联表服务类
 */
final class RdOrganizeUserService extends IService
{

    private string $jwt = 'miniProgram';

    public function __construct(
        protected readonly RdOrganizeUserRepository       $repository,
        protected readonly SmsService                     $smsService,
        protected readonly RdOrganizeUserRepository       $organizeUserRepository,
        protected readonly UserRepository                 $userRepository,
        protected readonly Factory                        $jwtFactory,
        protected readonly RoleService                    $roleService,
        protected readonly RdOrganizeEnterpriseRepository $enterpriseRepository,
        protected readonly AttachmentRepository           $attachmentRepository
    )
    {
    }

    public function getAllowedUpdateData($data): array
    {
        return array_flip(['enterprise_id', 'user_id', 'wechat_openid', 'extra_data', 'department', 'position', 'remark']);
    }

    public function loginByWx(array $data): array
    {
        if ($data['type'] == 2)
            $open_id = $this->wxMini($data['js_code']);
        elseif ($data['type'] == 3)
            $open_id = $this->wxApp($data['js_code']);
        else {
            throw new BusinessException(ResultCode::FAIL, '请选择正确的登录方式');
        }
        $user = $this->organizeUserRepository->getQuery()->where('wechat_openid', $open_id)->first();

        if ($user) {
            if ($user->user_id) {
//              已绑定手机号，返回token
                $user = $this->userRepository->findById($user->user_id);
                $jwt = $this->getJwt();
                return [
                    'access_token' => $jwt->builderAccessToken((string)$user->id)->toString(),
                    'refresh_token' => $jwt->builderRefreshToken((string)$user->id)->toString(),
                    'expire_at' => (int)$jwt->getConfig('ttl', 0),
                ];
            }
//          未绑定，返回待绑定信息
            return [
                'bind' => 0,
                'open_id' => $open_id
            ];
        }
//      未创建
        $this->create([
            'wechat_openid' => $open_id,
        ]);
        return [
            'bind' => 0,
            'open_id' => $open_id
        ];
    }

    public function wxMini(string $js_code): string
    {
        $c = array_column(Common::getConfigByGroup('wechat'), 'value', 'key');
        $config = [
            'app_id' => $c['mini_app_id'],
            'secret' => $c['mini_secret'],
            'token' => $c['mini_token'],
            'aes_key' => $c['mini_aes_key'],
            'http' => [
                'throw' => true, // 状态码非 200、300 时是否抛出异常，默认为开启
                'timeout' => 5.0,
                // 'base_uri' => 'https://api.weixin.qq.com/', // 如果你在国外想要覆盖默认的 url 的时候才使用，根据不同的模块配置不同的 uri
                'retry' => true, // 使用默认重试配置
            ],
        ];
        try {
            $app = new Application($config);
            $res = $app->getClient()->get('/sns/jscode2session', [
                'query' => [
                    'grant_type' => 'authorization_code',
                    'js_code' => $js_code,
                    'appid' => $c['mini_app_id'],
                    'secret' => $c['mini_secret'],
                ]
            ]);
            $res = json_decode($res->getContent(), true);
            return $res['openid'];
        } catch (\Throwable $e) {
            Common::logException(__CLASS__, $e, '请求结果 $res:' . json_encode($res, JSON_UNESCAPED_UNICODE));
            throw new BusinessException(ResultCode::FAIL, '登录失败');
        }

    }

    public function wxApp(string $js_code): string
    {
        $c = array_column(Common::getConfigByGroup('wechat'), 'value', 'key');
        $config = [
            'app_id' => $c['open_app_id'],
            'secret' => $c['open_app_secret'],
            'http' => [
                'throw' => true, // 状态码非 200、300 时是否抛出异常，默认为开启
                'timeout' => 5.0,
                // 'base_uri' => 'https://api.weixin.qq.com/', // 如果你在国外想要覆盖默认的 url 的时候才使用，根据不同的模块配置不同的 uri
                'retry' => true, // 使用默认重试配置
            ],
        ];
        try {
            $app = new Application($config);
            $res = $app->getClient()->get('/sns/oauth2/access_token', [
                'query' => [
                    'grant_type' => 'authorization_code',
                    'code' => $js_code,
                    'appid' => $c['open_app_id'],
                    'secret' => $c['open_app_secret'],
                ]
            ]);
            $res = json_decode($res->getContent(), true);
            return $res['openid'];
        } catch (\Throwable $e) {
            Common::logException(__CLASS__, $e, '请求结果 $res:' . json_encode($res, JSON_UNESCAPED_UNICODE));
            throw new BusinessException(ResultCode::FAIL, '登录失败');
        }

    }

    public function getJwt(): JwtInterface
    {
        return $this->jwtFactory->get($this->jwt);
    }

    public function bindPhone(array $data): array
    {
        $code = $data['code'] ?? '86';
        $phone = $data['phone'];
        $vCode = $data['vcode'];
        $open_id = $data['open_id'];
        $enterprise_id = $data['enterprise_id'] ?? 0;
        if (!env('APP_DEBUG')) {
            $service = new SmsService();
            $service->validate($phone, $vCode, $code, key: 'login', debug: 'bindWx');
        }
        $md = $this->repository->findByOpenId($open_id);
        if (!$md) throw new BusinessException(ResultCode::FORBIDDEN, '请先登录微信');
        if ($data['new_bind'] ?? 0) {
            $user = $this->userRepository->findById($md->user_id);
            if ($user) {
                $user->update([
                    'phone' => $phone,
                ]);
            }
            return [];
        } else {
            if ($md->user_id) throw new BusinessException(ResultCode::FAIL, '该微信已绑定手机号');
        }
//        查找手机号是否已存在用户
        $user = $this->userRepository->getQuery()->where('phone', $phone)->first();
        if (!$user) {
            $user = $this->userRepository->create([
                'username' => '小程序用户_' . $phone,
                'password' => '123456',
                'user_type' => Type::USER,
                'phone' => $phone,
                'created_by' => 0,
                'remark' => '小程序用户'
            ]);
        }

        $md->update([
            'user_id' => $user->id,
            'enterprise_id' => $enterprise_id
        ]);
        $jwt = $this->getJwt();
        return [
            'access_token' => $jwt->builderAccessToken((string)$user->id)->toString(),
            'refresh_token' => $jwt->builderRefreshToken((string)$user->id)->toString(),
            'expire_at' => (int)$jwt->getConfig('ttl', 0),
        ];
    }

    public function update(int $id, array $all)
    {
        Db::beginTransaction();
        try {
            $update = [];
            if ($enterprise = Arr::get($all, 'enterprise_id')) {
                if ($this->enterpriseRepository->findById($enterprise)->exists) {
                    $update['enterprise_id'] = $enterprise;
                }
            }
            if (Arr::get($all, 'remark')) {
                $update['remark'] = $all['remark'];
            }
            if (Arr::get($all, 'department')) {
                $update['department'] = $all['department'];
            }
            if (Arr::get($all, 'position')) {
                $update['position'] = $all['position'];
            }
            if (!empty($update)) {
                $this->repository->getQuery()->where('user_id', $id)->update($update);
            }

            $update = [];
            if ($name = Arr::get($all, 'username')) {
                $update['username'] = $name;
            }
            if ($avatar = Arr::get($all, 'avatar')) {
                $update['avatar'] = $avatar;
            }
            if (!empty($update)) {
                $this->userRepository->updateById($id, $update);
            }
            Db::commit();
        } catch (\Throwable $e) {
            Db::rollBack();
            Common::logException(__CLASS__, $e);
            throw new BusinessException(ResultCode::FAIL, '更新失败');
        }
        return 1;
    }

    public function vCode(array $data)
    {
        $code = $data['code'] ?? '86';
        $phone = $data['phone'];
        try {
            return $this->smsService->sendUniversalCode($phone, $code, key: 'login');
        } catch (\Throwable) {
            throw new BusinessException(ResultCode::FAIL, '验证码发送失败');
        }
    }

    public function loginByPhone(array $all)
    {
        $phone = $all['phone'];
        $vCode = $all['vcode'];
        $code = $all['code'] ?? '86';
        if (!env('APP_DEBUG')) {
            (new SmsService())->validate($phone, $vCode, $code, key: 'login', debug: 'loginByPhone');
        }
        $user = $this->userRepository->getQuery()->where('phone', $phone)->first();
        if (!$user) {
            $user = $this->userRepository->create([
                'username' => '小程序用户_' . $phone,
                'password' => '123456',
                'user_type' => Type::USER,
                'phone' => $phone,
                'created_by' => 0,
                'remark' => '小程序用户'
            ]);
            $this->create([
                'user_id' => $user->id,
            ]);
        }
        $jwt = $this->getJwt();
        return [
            'access_token' => $jwt->builderAccessToken((string)$user->id)->toString(),
            'refresh_token' => $jwt->builderRefreshToken((string)$user->id)->toString(),
            'expire_at' => (int)$jwt->getConfig('ttl', 0),
        ];
    }

    public function getUser($userId)
    {
        return $this->repository->getQuery()
            ->with(['user:id,username,avatar,email,status,user_type,phone,login_ip,login_time,remark,created_at,updated_at,nickname,signed,backend_setting', 'enterprise:id,name'])
            ->where('user_id', $userId)
            ->firstOrFail();
    }

}